package kaixo;





import com.ibm.saguaro.system.*;


public class Kaixo {
	//pribak
	static int in=0;
	static int repu=0;
	
	
	static long xmitDelay2;
	//LEds
	static Timer timerLEds;
    // time interval in ticks for toggleing a LED
    static long INTERVALLEDS;
    // LED index
    static byte idxLeds;
    //Leds
    static byte ledRojo=0;
    static byte ledAmarillo=0;
    static byte ledVerde=0;
    
    // the number of LEDs for the platform
    static int numLeds;
    // blink object required for the delegate callback
    //static Kaixo blink;
	
	//informacion Ruta para el IDS
	static int idsId=1;
	static int[] nora = {1,2,3,4,5};
	static int[] nundik = {1,2,3,3,3};
	static Nodo[] red;
	
	//informacion de historial paquetes
	static Paquete[] sendHistory = new Paquete[10];
	static Paquete[] recivedHistory = new Paquete[10];
	static int sendKont=0;
	static int recivedKont=0;	
	
	static int pak=0;
	//Informacion conexiones IDS
	static int[] conexionRealizada;
	static int[] conexionFallida;
	static int[] conexionNodo;
	static int[] estadoConexion;
	static int[] estadoNodo;
	
	//flags IDS
	static boolean ack=false;
	static boolean esperandoAck=false;
	static boolean enviado=false;
	static boolean listo=true;
	
	//Comunicaciones
	static Comunicaciones conection;
	
	//timers
	
	static Timer timer;
	
	//moterunnes
	static Kaixo rcount;
	static Timer tsend;
	static int i=0;
	static byte[] xmit;
	static long xmitDelay;
    static RadioTxDone onDone;
    // time interval in ticks for toggleing a LED
    static long INTERVAL;  
    
  //LIP
  	static int PORT = 161;
  	
  	// The LIP connection handle - the link to the host
  	static int lipConn;
      // The header information for LIP message plus context info of radio PDU
  	static byte[] liphdr;
  	// True if we have a process on the host
  	// which is picking up the forwarded radio frames.
  	static boolean listenerAttached; 
  	// Currently scanned channel.
  	static byte channel = (byte) 0xFF;
  	
  	static DataHandler eventHandler;
      
	static {
		//configura Leds
		idxLeds = 0;
		// get the number of LEDs
        numLeds = LED.getNumLEDs();
        // turn off all LEDs initially
    	//allLedsOff();

        // initialize object required for callback
       // blink = new Kaixo();
		
		//Termina configurar Leds
		rcount = new Kaixo();	
		
		// Crear Comunicaciones
		conection = new Comunicaciones(idsId,nora,nundik);
		//Crear Nodos
		red = new Nodo[10];
		red[0] = new Nodo(1,1);
		red[1] = new Nodo(2,2);
		red[2] = new Nodo(3,3);
		red[3] = new Nodo(4,3);
		red[4] = new Nodo(5,3);

		// acquire radio device
		Radio.acquire();
		
		// set the PAN ID to 0x22 and the short address 
		// to the last two bytes of the extended address
		Radio.setPanId(0x22, true);
		byte[] myAddrBytes = new byte[8];
		Radio.getExtAddr(myAddrBytes , 0);
		Radio.setShortAddr(Util.get16le(myAddrBytes, 0));

		// prepare beacon frame with src addressing
		xmit = new byte[12];
        xmit[0] = Radio.FCF_BEACON;
		xmit[1] = Radio.FCA_SRC_SADDR;
        Util.set16le(xmit, 3, 0x22);
		Util.set16le(xmit, 5, 0x1234);
                
            onDone = new RadioTxDone(null) {
			public void invoke(byte[] pdu, int len, int status, long txend) {
				Kaixo.transmitDone(pdu,len,status,txend);
			}
		};

		// put radio into receive mode on channel 0
		Radio.setRxHandler((byte)0, new RadioRxPdu(null){
			public void invoke(byte[] pdu, int len, long time, int quality){
				Kaixo.onRxPDU(pdu, len, time, quality);
			}
		});

		Radio.setRxDone(new RadioDone (null) {
			public void invoke (int info) { 
				Kaixo.onRxTimeout(info);
			}
		});

		Radio.enableRx(Time.currentTicks()+0x7FFFFFFF);
		
		// Check if this mote is attached to some host via some wire.
	    // If not attached then a radio sniffer doesn't make much sense - fail.
            if( LIP.isAvailable() == LIP.NONE )
		SystemException.throwIt(SystemException.NOT_SUPPORTED);
	    // Mote is attached to a host.
	    // Open a LIP communication over cable to the host.
         // open a LIP port and register callbacks.
    	int lipConn = LIP.open((byte)PORT, new DataHandler(null){
    			public int invoke(int info, byte[] data, int len) {
    				return handleLIP(info,data,len);
    			}
    		});
	    // Allocate a buffer for outgoing LIP header data and fill in source port.
	    // Source IP address is automatically filled in by OS.
	    //    LIPHDR    7    - required header by LIP protocol
	    //    TIME      4    - filled in per received PDU
	    //    LQI/RSSI  2    - ditto
	    //    FRAMELEN  1    - ditto
	    //
	    liphdr = new byte[LIP.PAYLOAD+7];
	    liphdr[LIP.MOTE_PORT] = (byte) PORT;
	    listenerAttached = false;

		// setup a periodic timer callback
		tsend = new Timer();
		tsend.setCallback(new TimerEvent(null){
			public void invoke(byte param, long time){			
				Kaixo.periodicSend(param, time);
			}
		});
		// convert the periodic delay from ms to platform ticks
		xmitDelay = Time.toTickSpan(Time.SECONDS, 10);
		// Start the timer
		tsend.setAlarmBySpan(xmitDelay);
		
		
		timerLEds = new Timer();
		timerLEds.setCallback(new TimerEvent(null){
			public void invoke(byte param, long time){
				Kaixo.blink(param,time);
			}
		});
		
		xmitDelay2 = Time.toTickSpan(Time.SECONDS, 1);
		timerLEds.setAlarmBySpan(xmitDelay2);
		
		
	    
	    Assembly.setDataHandler(new DataHandler(null) {
            @Override
            public int invoke(int arg0, byte[] arg1, int arg2) {
                    return handleLIP(arg0,arg1,arg2);
            }
	    });
	}
	
	public static void allLedsOff (byte param, long time) {
        // turn-off all LEDs
        for (byte i=0; i<numLeds; i++)
            LED.setState(i, (byte)0);
    }

 
    public static void blink (byte param, long time) {
    	LED.setState((byte)0, ledAmarillo);
    	LED.setState((byte)1, ledVerde);
    	LED.setState((byte)2, ledRojo);
    	
        timerLEds.setAlarmBySpan(xmitDelay2);
    }
    
    public static void insertarPaqueteEnviado(byte[] pdu){
    	sendHistory[sendKont] = new Paquete(pdu);
    	sendKont++;
    	sendKont%=10;
    }
    
    public static void insertarPaqueteRecivido(byte[] pdu){
    	recivedHistory[recivedKont] = new Paquete(pdu);
    	recivedKont++;
    	recivedKont%=10;
    }
    
    public static Paquete sacarPaqueteEnviado(int posicion){
    	if(sendHistory[posicion]!=null){
    		return sendHistory[posicion];
    	} else{
    		return null;
    	}
    }
    
    public static void EliminarPaqueteRecivido(Paquete paquete){
    	int i = 0;
    	boolean encontrado = false;
    	while ((i<10) && encontrado==false){
    		if (sendHistory[i]==paquete){
    			encontrado = true;
    			sendHistory[i]=null;
    		}
    		i++;
    	}
    }
    
    public static void EliminarPaqueteEnviado(Paquete paquete){
    	int i = 0;
    	boolean encontrado = false;
    	while ((i<10) && encontrado==false){
    		if (recivedHistory[i]==paquete){
    			encontrado = true;
    			recivedHistory[i]=null;
    		}
    		i++;
    	}
    }
    
    public static Paquete sacarPaqueteSinRespuesta(int Nodo){
    	int i=0;
    	boolean encontrado = false;
    	while((i<10) && (encontrado == false)){
    		if(sendHistory[i].getDestino()==Nodo){
    			encontrado = true;
    		}
    		i++;
    	}
    	if(encontrado == true){
    		return sendHistory[i-1];
    	} else {
    		return null;
    	}
    }
    
    public static Paquete sacarPaqueteRecivido(int posicion){
    	if(recivedHistory[posicion]!=null){
    		return recivedHistory[posicion];
    	} else{
    		return null;
    	}
    }
    
    
	
	// comprobar conexion pendientes
	public static void comprobarConexiones(){
		for(int i=0;i<estadoConexion.length;i++){
			
		}
	}
	
	
	//Si un paquete no es  para le nodo redirigelo.
	public static void redirigir(byte[] pdu,int len) {
		int rNork = (int)pdu[8];
		int rNora = (int)pdu[9];
		int rNundik = (int)pdu[10];
		//aldatu bidea
		xmit[10] =(byte)nundik[rNora];
		//birbidali mezua
		Radio.transmit(Radio.TXMODE_POWER_MAX, xmit, 0, 12, onDone);
		
	}

	// On a received pdu turn on the appropriate LEDs based on sequence number
	public static void onRxPDU (byte[] pdu, int len, long time, int quality) {
		conection.introducirPaqueteRecivido(pdu,pak);
		if ((pdu[9] != idsId)&&(pdu[8] != idsId)&&(conozcoRuta((int)pdu[9]))&&(pdu[10]==idsId)){
			redirigir(pdu, len);
			return;
		}else if(pdu[9] == idsId){
			paqueteRecivido(pdu,len,time,quality);			
		}else{
		
		}  	
		
	}
	
	public static void paqueteRecivido(byte[] pdu, int len, long time, int quality){
		int idEmisor = pdu[8];
		Nodo nodo = recuperarNodo(idEmisor);
		
		switch(pdu[11]){
		case 1:
			break;
		case 2:
			int reputacion = 8;
			enviarReputacion(pdu[8],pdu[12],reputacion);
			break;
		case 3:
			break;
		case 4:
			break;
		case 5:
			enviarACK(idEmisor);
			break;
		case 12:
			int no = (int)pdu[12];
			repu = (int)pdu[13];
			enviarReputacionLIP(pdu,len,time,quality,(int)pdu[12],(int)pdu[13]);
			ledRojo = 1;
			break;
		default:
			break;
		}
		
		if((pdu[11]==0)&&(nodo.isEsperandoACK()==true)){
			nodo.setEsperandoACK(false);
			nodo.sumarConexionesRealizadas(1);
		}else{
			/*
			if(pdu[11]==5){
				enviarACK(idEmisor);
				//enviar(idNodo,data);
				Nodo node = recuperarNodo(idEmisor);
				//conection.enviar(idNodo, data, node);
				//conection.enviar(2, (byte)3, node);
			}*/
		}
			
	}
	
	// We do not check the return code in this example
	public static void transmitDone(byte[] pdu, int len, int status, long txend){
		
	}
	
	private static void onTxDone(byte[] data, int len, int status, long txend) {
		
	}

	// Re-enable frame reception after reception timeout	
	public static void onRxTimeout (int info) {
		// Re-enable frame reception
		Radio.enableRx(Time.currentTicks()+0x7FFFFFFF);
	}
	
	//Recuperar Nodo
	public static Nodo recuperarNodo(int id){
		for(int i=0; i<red.length;i++){
			if (red[i]!=null){
				if (red[i].getId()==id) {
					return red[i];
				}
			}				
		}
		return new Nodo(0,0);
	}
	
	//enviar reputacion LIP	
	
	public static void enviarReputacionLIP(byte[] pdu, int len, long time, int quality, int nodo, int reputacion){
		byte[] hdr = liphdr;
	    byte[] xmit = new byte[10];
	    for(int i=0;i<xmit.length;i++){
	    xmit[i] = (byte)i;
	    }
	    xmit[3] = (byte)1;
	    xmit[4] = (byte)nodo;
	    xmit[5] = (byte)reputacion;
	    Util.set32be(hdr,LIP.PAYLOAD+0,time);
	    // Util.set16be(hdr,LIP.PAYLOAD+4,quality);  // hi:LQI lo:RSSI
	    //hdr[LIP.PAYLOAD+6] = (byte)len;
	    LIP.send(hdr,LIP.PAYLOAD+4,  // LIP header plus context
		     xmit,0,xmit.length);  // radio PDU
	    
	}
	
	//enviar reputacion LIP
	public static void enviarRepuLip(byte[] pdu){
		byte[] hdr = liphdr;
	    byte[] xmit = new byte[10];
	    //nodoEmisor
	    xmit[0] = pdu[8];
	    xmit[1] = pdu[9];
	    xmit[2] = pdu[10];
	    xmit[3] = pdu[1];
	    xmit[4] = pdu[12];
	    xmit[5] = pdu[13];
	    //xmit[4] = (byte)nodo;
	    //xmit[5] = (byte)reputacion;
	    //Util.set32be(hdr,LIP.PAYLOAD+0,time);
	    //Util.set16be(hdr,LIP.PAYLOAD+4,quality);  // hi:LQI lo:RSSI
	    //hdr[LIP.PAYLOAD+6] = (byte)0;
	    LIP.send(hdr,LIP.PAYLOAD+0,  // LIP header plus context
		     xmit,0,xmit.length);  // radio PDU
	    
	}
	
	//funcion para enviar reputacion a un nodo
		public static void enviarReputacion(int idNodo, int idNodoReputacion,int reputacion){
			// prepare beacon frame with src addressing
			xmit = new byte[14];
			xmit[0] = Radio.FCF_BEACON;
			xmit[1] = Radio.FCA_SRC_SADDR;
			Util.set16le(xmit, 3, 0x22);
			Util.set16le(xmit, 5, 0x1234);
			
			//prepara cabecera para IDS
			xmit[8] = (byte)idsId; 
			xmit[9] = (byte)idNodo;
			xmit[10] = (byte)nundik[idNodo-1];
			xmit[11] = (byte)12;
			xmit[12] = (byte)idNodoReputacion;
			xmit[13] = (byte)reputacion;
			Radio.transmit(Radio.TXMODE_POWER_MAX, xmit, 0, 14, onDone);
			
		}
	
	//funcion para enviar datos a un nodo
	public static void enviar(int idNodo, byte info){
		// prepare beacon frame with src addressing
		xmit = new byte[13];
		xmit[0] = Radio.FCF_BEACON;
		xmit[1] = Radio.FCA_SRC_SADDR;
		Util.set16le(xmit, 3, 0x22);
		Util.set16le(xmit, 5, 0x1234);
		
		//recuperar nodo
		Nodo nod = recuperarNodo(idNodo);
		
		//prepara cabecera para IDS
		xmit[8] = (byte)idsId; 
		xmit[9] = (byte)idNodo;
		xmit[10] = (byte)nundik[idNodo-1];
		xmit[11] = (byte)5;
		xmit[12] = info;
		Radio.transmit(Radio.TXMODE_POWER_MAX, xmit, 0, 13, onDone);
		recuperarNodo(idNodo).setEsperandoACK(true);
		
	}
	
	//funcion para pedir informacion a un nodo
	public static void pedirInfo(int idNodo){
		// prepare beacon frame with src addressing
		xmit = new byte[12];
		xmit[0] = Radio.FCF_BEACON;
		xmit[1] = Radio.FCA_SRC_SADDR;
		Util.set16le(xmit, 3, 0x22);
		Util.set16le(xmit, 5, 0x1234);
						
		//prepara cabecera para IDS
		xmit[8] = (byte)idsId; 
		xmit[9] = (byte)idNodo;
		xmit[10] = (byte)nundik[idNodo-1];
		xmit[11] = (byte)1;
		Radio.transmit(Radio.TXMODE_POWER_MAX, xmit, 0, 12, onDone);
	}
	//funcion para pedir la reputacion a un nodo
	public static void pedirReputacion(int idNodo,int idNodoReputacion){
		// prepare beacon frame with src addressing
		xmit = new byte[13];
		xmit[0] = Radio.FCF_BEACON;
		xmit[1] = Radio.FCA_SRC_SADDR;
		Util.set16le(xmit, 3, 0x22);
		Util.set16le(xmit, 5, 0x1234);
								
		//prepara cabecera para IDS
		xmit[8] = (byte)idsId; 
		xmit[9] = (byte)idNodo;
		xmit[10] = (byte)nundik[idNodo-1];
		xmit[11] = (byte)2;
		xmit[12] = (byte)idNodoReputacion;
		Radio.transmit(Radio.TXMODE_POWER_MAX, xmit, 0, 13, onDone);
	}
	//funcion para pedir en broadcasta la reputacion acerca de un nodo
	public static void pedirReputacionTodos(int idNodo){
		// prepare beacon frame with src addressing
		xmit = new byte[12];
		xmit[0] = Radio.FCF_BEACON;
		xmit[1] = Radio.FCA_SRC_SADDR;
		Util.set16le(xmit, 3, 0x22);
		Util.set16le(xmit, 5, 0x1234);
								
		//prepara cabecera para IDS
		xmit[8] = (byte)idsId; 
		xmit[9] = (byte)idNodo;
		xmit[10] = (byte)nundik[idNodo-1];
		xmit[11] = (byte)3;
		
		insertarPaqueteEnviado(xmit);
		
		Radio.transmit(Radio.TXMODE_POWER_MAX, xmit, 0, 12, onDone);
	}
	
	//pedir a los nodos que estan al lado la informacion acerca de los nodos que componen el vecindario.
	public static void pedirVecindario(int idNodo){
		// prepare beacon frame with src addressing
		xmit = new byte[13];
		xmit[0] = Radio.FCF_BEACON;
		xmit[1] = Radio.FCA_SRC_SADDR;
		Util.set16le(xmit, 3, 0x22);
		Util.set16le(xmit, 5, 0x1234);
								
		//prepara cabecera para IDS
		xmit[8] = (byte)idsId; 
		xmit[9] = (byte)0;
		xmit[10] = (byte)0;
		xmit[11] = (byte)4;
		xmit[12] = (byte)idNodo;
		Radio.transmit(Radio.TXMODE_POWER_MAX, xmit, 0, 13, onDone);
	}
	
	//buscar en tabla de enroutamiento si se encuntra la ruta de un nodo
	public static boolean conozcoRuta(int idNodo){
		
		for(int i=0;i<nora.length;i++){
			if(nora[i]==idNodo){
				return true;
			}
		}
		return false;
	}
	
	//enviar ack
	public static void enviarACK(int idNodo){
		// prepare beacon frame with src addressing
		xmit = new byte[12];
		xmit[0] = Radio.FCF_BEACON;
		xmit[1] = Radio.FCA_SRC_SADDR;
		Util.set16le(xmit, 3, 0x22);
		Util.set16le(xmit, 5, 0x1234);
		
		//prepara cabecera para IDS
		xmit[8] = (byte)idsId; 
		xmit[9] = (byte)idNodo;
		xmit[10] = (byte)nundik[idNodo-1];
		xmit[11] = (byte)0;
		Radio.transmit(Radio.TXMODE_POWER_MAX, xmit, 0, 12, onDone);
	}
	
	public static boolean flowEnviar(int idNodo, byte data){
		if(listo==true){
			//enviar(idNodo,data);
			Nodo node = recuperarNodo(idNodo);
			//conection.enviar(idNodo, data, node);
			enviar(idNodo, data);
			if(ledVerde==0){
				ledVerde=1;
			} else{
				ledVerde=0;
			}
		} else {
			return false;
		}
		return true;
	}
	
	static int handleLIP (int info, byte[] data, int len) {
		    Util.copyData(data, 0, liphdr, 0, LIP.MOTE_PORT);
		    listenerAttached = true;
		    // Report our local time granularity
		    Util.set32be(data,LIP.PAYLOAD+0,Time.toTickSpan(Time.SECONDS,1));
		    // Send back input buffer with new length
		    return (int)(LIP.PAYLOAD+4); 
        }
	
	// Called on a timer alarm
	public static void periodicSend(byte param, long time) {
		/*
		xmit[8] = (byte)idsId; 
		xmit[9] = (byte)3;
		xmit[10] = (byte)2;
		// increment color
		byte[] galdera = new byte[1];
		galdera[0] = (byte)1;
		// set sequence number
		//xmit[2] = galdera;
		xmit[11] = (byte)i;
		i++;
		if (i==3){
			i=0;
		}
		*/
			// acquire radio device
				//Radio.acquire();
			
			// set the PAN ID to 0x22 and the short address 
			// to the last two bytes of the extended address
				//Radio.setPanId(0x22, true);
				//byte[] myAddrBytes = new byte[8];
				//Radio.getExtAddr(myAddrBytes , 0);
				//Radio.setShortAddr(Util.get16le(myAddrBytes, 0));
		//i++;
		pedirReputacion(2,2);
		//i%=3;
		//proba
		/*Paquete[] na = new Paquete[125];
		int i=0;
		while(true){
			na[i] = new Paquete(xmit);
			i++;
		}*/
		//LED.setState((byte)0, (byte)1);
		
		// send the message 
		//Radio.transmit(0, xmit, 1, galdera, 0, 1, onDone);
		//Radio.transmit(Radio.TXMODE_POWER_MAX, xmit, 0, 12, onDone);
		// Setup a new alarm
		tsend.setAlarmBySpan(xmitDelay);
	}

}
